package cn.amossun.starter.event.handler;

import com.alibaba.fastjson.JSON;
import cn.amossun.starter.event.annoation.EventPublisher;
import cn.amossun.starter.event.common.Constants;
import cn.amossun.starter.event.exception.EventPushRemoteException;
import cn.amossun.starter.event.property.SimpleEventMessage;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Aspect
public class SimplePublisherAspect implements RemoteEventMessagePublisher<SimpleEventMessage>{

    @Value("${spring.application.name}")
    private String service;

    @Autowired
    @Qualifier(value = "instance")
    private String instance;

    @Autowired
    @Qualifier("eventRabbitTemplate")
    RabbitTemplate rabbitTemplate;

    @Pointcut("@annotation(cn.amossun.starter.event.annoation.EventPublisher)")
    public void pointcut() {
        log.info("拦截事件发布");
    }

    @AfterReturning(pointcut = "pointcut()", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        EventPublisher eventPublisher = AnnotationUtils.findAnnotation(method, EventPublisher.class);
        boolean thrownException = eventPublisher.thrownException();

        try {
            String message = JSON.toJSONString(result);

            Map<String, Object> argumentsMap = getArgumentsMap(eventPublisher);

            String topic = getRoutingKey(eventPublisher);

            SimpleEventMessage eventMessage = eventMessage(service, instance, topic,
                    eventPublisher.definition(), eventPublisher.description(),
                    method.getName(), message, argumentsMap);

            String routingKey = String.format(Constants.ROUTING_KEY_TEMPLATE, topic, eventPublisher.definition().getCode());
            pushRemoteEvent(getExchangeName(eventPublisher), routingKey, eventMessage);

        } catch (Exception e) {
            log.error("发布事件失败: ", e);
            if (thrownException) {
                throw new EventPushRemoteException(e.getMessage());
            }
        }
    }

    @Override
    public void pushRemoteEvent(String exchangeName, String routingKey, SimpleEventMessage event) {

        String eventStr = JSON.toJSONString(event);

        log.info("开始发布事件: routing key: [{}] => {}", routingKey, event);

        CorrelationData correlationData = new CorrelationData(String.format(Constants.QUEUE_UNIQUE_TEMPLATE, routingKey, UUID.randomUUID().toString()));
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType(Constants.CONTENT_TYPE_JSON);
        messageProperties.setContentEncoding(Constants.DEFAULT_CHARSET);
        Message rabbitMessage = new Message(eventStr.getBytes(Charset.forName(Constants.DEFAULT_CHARSET)), messageProperties);

        try {
            //send message by custom routingKey and custom exchange
            rabbitTemplate.send(exchangeName, routingKey, rabbitMessage, correlationData);

            log.info("发布事件成功: routing key: [{}] => {}", routingKey, event);
        } catch (AmqpException e) {
            throw e;
        }
    }
}
